/************************************************************************
* image_bmp.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#include "graphics.h"
#include "file.h"
#include "path.h"

typedef struct bmp_file_header_s {
	short int type;
	unsigned int size;
	short int reserved1;
	short int reserved2;
	unsigned int offset;
} __attribute__((packed)) bmp_file_header_t;

typedef struct bmp_info_header_s {
	unsigned int size;
	int width;
	int height;
	short int planes;
	short int bpp;
	unsigned int compression;
	unsigned int img_size;
	int hres;
	int vres;
	unsigned int palette_colors;
	unsigned int important_colors;
} __attribute__((packed)) bmp_info_header_t;

/* is it a bmp image? */
int image_is_bmp(file_t *f)
{
	bmp_file_header_t bmfh;
	file_read(f,&bmfh,sizeof(bmp_file_header_t));
	file_seek(f,0,SEEK_SET);
	if (bmfh.type == 19778)
		return 1;
	return 0;
}

/* load a bitmap image to a texture */
int image_load_bmp(file_t *f, image_t *p)
{
	int i;
	int j;
	unsigned char* p_data;
	colour_t *colours;
	bmp_info_header_t bmih;
	bmp_file_header_t bmfh;
	unsigned int size;
	int offset;
	int pad;
	int x;
	int y;

	file_read(f,&bmfh,sizeof(bmp_file_header_t));
	file_read(f,&bmih,sizeof(bmp_info_header_t));

	p->w = bmih.width;
	p->h = bmih.height;

	size = (p->w*p->h*(unsigned int)(bmih.bpp/8.0));

	/* unsupported format */
	if (bmih.bpp < 8)
		return 1;

	/* get the colour index for 8bit images */
	if (bmih.bpp == 8)
		colours = file_get(f);

	pad = (int)((float)p->w*(float)bmih.bpp/8.0);
	offset = 0;

	while (pad%4) {
		pad++;
		offset++;
	}

	p->pixels = malloc(sizeof(unsigned char)*(p->w*p->h*4));

	if (!p->pixels)
		return 1;

	file_seek(f,bmfh.offset,SEEK_SET);
	p_data = file_get(f);

	/* convert whatever pixel format the image is in to 32 bit RGBA */

	/* 8 bits per pixel */
	if (bmih.bpp == 8) {
		if (p->h>0) {
			int t = size*8;
			j=0;
			for (i=0; i<t;i+=4) {
				if ((i+1)%pad == 0)
					i += offset;
				p->pixels[i] = colours[p_data[j]].b;
				p->pixels[i+1] = colours[p_data[j]].g;
				p->pixels[i+2] = colours[p_data[j]].r;
				p->pixels[i+3] = 255;
				j++;
			}
		}else{
			int t = size*8;
			j = size-1;
			for (i=0; i<t; i+=4) {
				if ((i+1)%pad == 0)
					i += offset;
				p->pixels[i] = colours[p_data[j]].b;
				p->pixels[i+1] = colours[p_data[j]].g;
				p->pixels[i+2] = colours[p_data[j]].r;
				p->pixels[i+3] = 255;
				j--;
			}
		}
	/* 24 bits per pixel */
	}else if (bmih.bpp == 24) {
		if (p->h>0) {
			for (i=0, j=0; j<size; j+=3, i+=4) {
				if ((j+1)%pad == 0)
					j += offset;
				p->pixels[i+3] = 255;
				p->pixels[i+2] = p_data[j];
				p->pixels[i+1] = p_data[j+1];
				p->pixels[i] = p_data[j+2];
			}
		}else{
			j = size-3;
			for (i=0; i<size; i+=4) {
				/* if ((i+1)%pad == 0)
					i += offset; */
				p->pixels[i+3] = 255;
				p->pixels[j+2] = p_data[i];
				p->pixels[j+1] = p_data[i+1];
				p->pixels[j] = p_data[i+2];
				j-=3;
			}
		}
	/* 32 bits per pixel */
	}else if (bmih.bpp == 32) {
		if (p->h>0) {
			i = 0;
			for (y=p->h-1; y>-1; y--) {
				for (x=0; x<p->w; x++) {
					if ((i+1)%pad == 0)
						i += offset;
					j = ((y*p->w)+x)*4;
					p->pixels[j] = p_data[i+2];
					p->pixels[j+1] = p_data[i+1];
					p->pixels[j+2] = p_data[i];
					p->pixels[j+3] = p_data[i+3];
					i += 4;
				}
			}
		}else{
			j = size-4;
			for (i=0; i<size; i+=4) {
				if ((i+1)%pad == 0)
					i += offset;
				p->pixels[j+3] = p_data[i+3];
				p->pixels[j+2] = p_data[i];
				p->pixels[j+1] = p_data[i+1];
				p->pixels[j] = p_data[i+2];
				j-=4;
			}
		}
	/* unsupported bits per pixel */
	}else{
		return 1;
	}

	return 0;
}

/* write pixel data to a bmp image */
int image_save_bmp(image_t *p, char* file)
{
	char buff[2048];
	FILE *f;
	int i;
	bmp_file_header_t bmfh;
	bmp_info_header_t bmih;

	bmih.size = sizeof(bmp_info_header_t);
	bmih.width = p->w;
	bmih.height = p->h;
	bmih.planes = 1;
	bmih.bpp = 32;
	bmih.compression = 0;
	bmih.img_size = p->w*p->h*4;
	bmih.hres = 0;
	bmih.vres = 0;
	bmih.palette_colors = 0;
	bmih.important_colors = 0;

	bmfh.type = 19778;
	bmfh.size = sizeof(bmp_file_header_t)+sizeof(bmp_info_header_t)+bmih.img_size;
	bmfh.reserved1 = 0;
	bmfh.reserved2 = 0;
	bmfh.offset = sizeof(bmp_file_header_t)+sizeof(bmp_info_header_t);

	if (!path_get(NULL,file,0,buff,2048))
		return 1;
	f = fopen(buff,"wb");
	if (!f)
		return 1;

	fwrite(&bmfh,sizeof(bmp_file_header_t),1,f);
	fwrite(&bmih,sizeof(bmp_info_header_t),1,f);

	/* bmp expects BGRA so change it from RGBA */
	for (i=0; i<bmih.img_size; i+=4) {
		fwrite(&p->pixels[i+2],1,1,f);
		fwrite(&p->pixels[i+1],1,1,f);
		fwrite(&p->pixels[i],1,1,f);
		fwrite(&p->pixels[i+3],1,1,f);
	}

	fclose(f);

	return 0;
}
